package com.byit.shrio.realm;

import com.byit.domain.SysUser;
import com.byit.service.ISysRoleMeunService;
import com.byit.service.ISysRoleService;
import com.byit.service.ISysUserService;
import com.byit.shrio.session.SessionListener;
import com.byit.utils.ShiroUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpSession;
import java.util.Collection;

/**
 * 自定义权限匹配和账号密码匹配
 *
 * @author wangx
 */

public class ShiroRealm extends AuthorizingRealm {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysRoleService roleService;

    @Autowired
    private ISysRoleMeunService roleMeunService;

    @Autowired
    private SessionListener sessionListener;

    /**
     * 此方法调用  hasRole,hasPermission的时候才会进行回调.
     * <p/>
     * 权限信息.(授权):
     * 1、如果用户正常退出，缓存自动清空；
     * 2、如果用户非正常退出，缓存自动清空；
     * 3、如果我们修改了用户的权限，而用户不退出系统，修改的权限无法立即生效。
     * （需要手动编程进行实现；放在service进行调用）
     * 在权限修改后调用realm中的方法，realm已经由spring管理，所以从spring中获取realm实例，
     * 调用clearCached方法；
     * :Authorization 是授权访问控制，用于对用户进行的操作授权，证明该用户是否允许进行当前操作，如访问某个链接，某个资源文件等。
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        /*
         * 当没有使用缓存的时候，不断刷新页面的话，这个代码会不断执行， 当其实没有必要每次都重新设置权限信息，所以我们需要放到缓存中进行管理；
		 * 当放到缓存中时，这样的话，doGetAuthorizationInfo就只会执行一次了， 缓存过期之后会再次执行。
		 */
        //// SysUser user = (SysUser) principals.getPrimaryPrincipal();

        // 实际项目中，这里可以根据实际情况做缓存，如果不做，Shiro自己也是有时间间隔机制，2分钟内不会重复执行该方法
        // 根据用户查询用户的角色与权限。
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        Long userId = ShiroUtils.getUserId();

        authorizationInfo.addRoles(roleService.getAuthUserRole(userId));

        authorizationInfo.addStringPermissions(roleMeunService.getAuthUserMenu(userId));

        return authorizationInfo;
    }

    /**
     * 认证登录
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //// 获取用户的输入的账号.
        String loginName = (String) token.getPrincipal();

        Subject subject = SecurityUtils.getSubject();
        Session nowSession = subject.getSession();
        Collection<HttpSession> sessions = sessionListener.getActiveSessions();
        if(sessions != null && sessions.size() > 0) {
            for (HttpSession session : sessions) {
                if (!nowSession.getId().equals(session.getId()) && loginName.equals(session.getAttribute("loginName"))) {
                    logger.info("================删除重复登录的用户================");
                    ////sessionDAO.delete(session);
                }
            }
        }

        SysUser user = userService.userLogin(loginName);

        if (null == user) {
            return null;
        }

        if (user.getStatus() == 0) {
            //// 账户已被禁用
            throw new LockedAccountException();
        }

        SimpleAuthenticationInfo authentication = new SimpleAuthenticationInfo(user, user.getPassword(), getName());

        return authentication;
    }

    /**
     * 清理缓存权限
     */
    public void clearCachedAuthorizationInfo() {
        this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
    }

}
